package sanmubird.club.v8.FunctionParam.methodReference;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Test;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.*;

/**
 * 方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。
 * 它的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。
 * 方法引用就是让你根据已有的方法实现来创建Lambda表达式。但是,显式地指明方法的名称,你的代码的可读性会更好。
 * eg:
 * Apple::getWeight 就是引用了 Apple 类中定义的方法 getWeight 。
 * 方法引用就是Lambda表达式 (Apple a) -> a.getWeight() 的快捷写法。
 */

interface logging<T> extends Consumer<T> {

    @Override
    void accept(T var1);


}


class Console {
    static <T, R> void log2(Function<T, R> function, T t) {
        System.out.print(function.apply(t));
    }

    static <T, R> void log(Object obj) {
        System.out.print(obj);
    }
}

public class MethodReference {

    @Test
    public void testMethod() {
        String[] as = {"hello ", "world ", "!"};
        List<String> ls = Arrays.asList(as);

        // 静态方法
        ls.forEach(System.out::print);
        System.out.println();

        // 普通方法
        ls.sort(Comparator.comparing(String::length).reversed().thenComparing(String::hashCode));

        // 这里我想实现一个打印 可以灵活的打印 其任意的属性
        Consumer<String> consumer = s -> System.out.print(s.length());
        ls.forEach(consumer);
    }

    @Test
    public void testConstruct() {
        // 构造方法
        // 无餐构造 ()-> Person
        Supplier<Person> supplier = Person::new;
        Person p = supplier.get();

        // 一个参数的构造方法 (name) -> Person
        Function<String, Person> functionPerson = Person::new;
        Person p1 = functionPerson.apply("张三丰");

        // 两个参数的构造方法 (name, gender) -> Person;
        BiFunction<String, Integer, Person> biFunction = Person::new;
        Person p2 = biFunction.apply("张三丰", 1);

        // 三个参数的构造方法 (name, gender, age) -> Person;  这里自定义了
        TriFunction<String, Integer, Integer, Person> triFunction = Person::new;
        Person p3 = triFunction.apply("张三丰", 1, 100);
        System.out.println(p3);
    }

    /**
     * 函数式接口 的复杂用法
     */
    @Test
    public void testComplex() {
        Function<Integer, Integer> f = x -> x + 1;
        Function<Integer, Integer> g = x -> x * 2;
        // 类似于 数学上的 g(f(x))
        Function<Integer, Integer> h1 = f.andThen(g);
        // 类似于 数学上的 f(g(x))
        Function<Integer, Integer> h2 = f.compose(g);
        int result1 = h1.apply(1);
        int result2 = h2.apply(1);
        System.out.println(result1);
        System.out.println(result2);

    }

}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
    String name;
    int gender;
    int age;

    public Person(String name) {
        this.name = name;
    }

    public Person(String name, int gender) {
        this.name = name;
        this.gender = gender;
    }
}

interface TriFunction<T, U, V, R> {
    R apply(T t, U u, V v);
}
